#include "button.h"
#define DEBUG 0
#if DEBUG
    #define    DEBUG_PRINTF(...) printf(##__VA_ARGS__)
#else
    #define    DEBUG_PRINTF(...)
#endif
#ifndef NULL
#define NULL 0
#endif

#define EVENT_SET_AND_EXEC_CB(btn, evt)                                        \
    do                                                                         \
    {                                                                          \
        btn->event = evt;                                                      \
        if(btn->cb)                                                            \
            btn->cb((button_t*)btn);                                      \
    } while(0)

/**
 * BTN_IS_PRESSED
 * 
 * 1: is pressed
 * 0: is not pressed
*/
#define BTN_IS_PRESSED(i) (g_btn_status_reg & (1 << i))

enum BTN_STAGE
{
    BTN_STAGE_DEFAULT = 0,
    BTN_STAGE_DOWN    = 1,
    BTN_STAGE_MULTIPLE_CLICK = 2
};

typedef uint32_t btn_type_t;

static button_t *btn_head = NULL;

/**
 * g_logic_level
 * 
 * The logic level of the button pressed, 
 * Each bit represents a button.
 * 
 * First registered button, the logic level of the button pressed is 
 * at the low bit of g_logic_level.
*/
btn_type_t g_logic_level = (btn_type_t)0;

/**
 * g_btn_status_reg
 * 
 * The status register of all button, each bit records the pressing state of a button.
 * 
 * First registered button, the pressing state of the button is 
 * at the low bit of g_btn_status_reg.
*/
btn_type_t g_btn_status_reg = (btn_type_t)0;

static uint8_t button_cnt = 0;



/**
 * @brief 按键事件名定义
 * 
 */
char *enum_event_string[] = {
    ENUM_TO_STR(BTN_PRESS_DOWN),
    ENUM_TO_STR(BTN_PRESS_CLICK),
    ENUM_TO_STR(BTN_PRESS_DOUBLE_CLICK),
    ENUM_TO_STR(BTN_PRESS_REPEAT_CLICK),
    ENUM_TO_STR(BTN_PRESS_SHORT_START),
    ENUM_TO_STR(BTN_PRESS_SHORT_UP),
    ENUM_TO_STR(BTN_PRESS_LONG_START),
    ENUM_TO_STR(BTN_PRESS_LONG_UP),
    ENUM_TO_STR(BTN_PRESS_LONG_HOLD),
    ENUM_TO_STR(BTN_PRESS_LONG_HOLD_UP),
    ENUM_TO_STR(BTN_PRESS_MAX),
    ENUM_TO_STR(BTN_PRESS_NONE),
};
/**按键ID名定义*/
char *enum_btn_id_string[] = {
    ENUM_TO_STR(USER_BUTTON_UP),
    ENUM_TO_STR(USER_BUTTON_DOWN),
    ENUM_TO_STR(USER_BUTTON_LEFT),
    ENUM_TO_STR(USER_BUTTON_RIGHT),
    ENUM_TO_STR(USER_BUTTON_MENU),
    ENUM_TO_STR(USER_BUTTON_ENTER),
    ENUM_TO_STR(USER_BUTTON_RUN),
    ENUM_TO_STR(USER_BUTTON_STOP),
    ENUM_TO_STR(USER_BUTTON_MAX),
};

button_t user_button[USER_BUTTON_MAX];

static uint8_t common_btn_read(void *arg)
{
    uint8_t value = 0;

    button_t *btn = (button_t *)arg;

    switch (btn->id)
    {
    case USER_BUTTON_UP:
        //value = (GPIOG->IDR & GPIO_PIN_11) ? 0 : 1;
        break;
    case USER_BUTTON_DOWN:
        //value = (GPIOD->IDR & GPIO_PIN_6) ? 0 : 1;
        break;
    case USER_BUTTON_LEFT:
        //value = (GPIOG->IDR & GPIO_PIN_14) ? 0 : 1;
        break;
    case USER_BUTTON_RIGHT:
        //value = (GPIOG->IDR & GPIO_PIN_13) ? 0 : 1;
        break;
    case USER_BUTTON_MENU:
        //value = (GPIOA->IDR & GPIO_PIN_3) ? 0 : 1;
        break;
    case USER_BUTTON_ENTER:
        //value = (GPIOB->IDR & GPIO_PIN_15) ? 0 : 1;
        break;
    case USER_BUTTON_RUN:
        //value = (GPIOB->IDR & GPIO_PIN_14) ? 0 : 1;
        break;
    case USER_BUTTON_STOP:
        //value = (GPIOB->IDR & GPIO_PIN_13) ? 0 : 1;
        break;
    default:
        value = 0;
        break;
    }

    return value;
}
__weak void common_btn_evt_cb(void *arg)
{
    button_t *btn = (button_t *)arg;
    //    button_msg_t btnmsg = {0};
    //    btnmsg.str.id = btn->id;
    //    btnmsg.str.event = btn->event;
    //    btnmsg.str.click_cnt = btn->click_cnt;
    /*switch(btn->id)
    {
    case USER_BUTTON_UP:wmsg.MsgId = ID_BUTTON_UP;break;
    case USER_BUTTON_DOWN:wmsg.MsgId = ID_BUTTON_DOWN;break;
    case USER_BUTTON_LEFT:wmsg.MsgId = ID_BUTTON_LEFT;break;
    case USER_BUTTON_RIGHT:wmsg.MsgId = ID_BUTTON_RIGHT;break;
    case USER_BUTTON_MENU:wmsg.MsgId = ID_BUTTON_MENU;break;
    case USER_BUTTON_ENTER:wmsg.MsgId = ID_BUTTON_ENTER;break;
    case USER_BUTTON_RUN:wmsg.MsgId = ID_BUTTON_RUN;break;
    case USER_BUTTON_STOP:wmsg.MsgId = ID_BUTTON_STOP;break;
    default:break;
    }*/
   /*switch(btn->event)
   {
   case BTN_PRESS_DOWN:GUI_SendKeyMsg(key_list[btn->id&7],1);break;
   case BTN_PRESS_CLICK:GUI_SendKeyMsg(key_list[btn->id&7],0);break;
   case BTN_PRESS_DOUBLE_CLICK:GUI_SendKeyMsg(key_list[btn->id&7],0);break;
   case BTN_PRESS_REPEAT_CLICK:GUI_SendKeyMsg(key_list[btn->id&7],0);break;
   case BTN_PRESS_SHORT_START:break;
   case BTN_PRESS_SHORT_UP:GUI_SendKeyMsg(key_list[btn->id&7],0);break;
   case BTN_PRESS_LONG_START:break;
   case BTN_PRESS_LONG_UP:GUI_SendKeyMsg(key_list[btn->id&7],0);break;
   case BTN_PRESS_LONG_HOLD:break;
   case BTN_PRESS_LONG_HOLD_UP:GUI_SendKeyMsg(key_list[btn->id&7],0);break;
   default:break;
   }*/
//    WM_SendMessage(hWin,&wmsg);
    //    osMessageQueuePut(s_pbd->mqid, &btnmsg, NULL, 5);
    DEBUG_PRINTF("id:[%s] event:[%30s] repeat:%d\n",
                 enum_btn_id_string[btn->id],
                 enum_event_string[btn->event],
                 btn->click_cnt);
    if ((button_event_read(&user_button[USER_BUTTON_UP]) == BTN_PRESS_CLICK) &&
        (button_event_read(&user_button[USER_BUTTON_LEFT]) == BTN_PRESS_CLICK))
    {
        //        btnmsg.str.comb = 1;
        //        btnmsg.str.id = user_button[USER_BUTTON_UP].id;
        //        btnmsg.str.id2 = user_button[USER_BUTTON_LEFT].id;
        //        osMessageQueuePut(s_pbd->mqid, &btnmsg, NULL, 5);
        DEBUG_PRINTF("[combination]: button up and left\n");
    }
}
void button_init(void)
{
    for (int i = 0; i < USER_BUTTON_MAX; i++)
    {
        user_button[i].id = i;
        user_button[i].usr_button_read = common_btn_read;
        user_button[i].cb = common_btn_evt_cb;
        user_button[i].pressed_logic_level = 1;
        user_button[i].short_press_start_tick = MS_TO_SCAN_CNT(1500);
        user_button[i].long_press_start_tick = MS_TO_SCAN_CNT(3000);
        user_button[i].long_hold_start_tick = MS_TO_SCAN_CNT(4500);

        if (i == USER_BUTTON_DOWN)
        {
            // user_button[USER_BUTTON_DOWN].pressed_logic_level = 0;
        }

        button_register(&user_button[i]);
    }
}
/**
 * @brief Register a user button
 * 
 * @param button: button structure instance
 * @return Number of keys that have been registered, or -1 when error
*/
int32_t button_register(button_t *button)
{
    button_t *curr = btn_head;
    
    if (!button || (button_cnt > sizeof(btn_type_t) * 8))
    {
        return -1;
    }

    while (curr)
    {
        if(curr == button)
        {
            return -1;  /* already exist. */
        }
        curr = curr->next;
    }

    /**
     * First registered button is at the end of the 'linked list'.
     * btn_head points to the head of the 'linked list'.
    */
    button->next = btn_head;
    button->status = BTN_STAGE_DEFAULT;
    button->event = BTN_PRESS_NONE;
    button->scan_cnt = 0;
    button->click_cnt = 0;
    button->max_multiple_clicks_interval = MAX_MULTIPLE_CLICKS_INTERVAL;
    btn_head = button;

    /**
     * First registered button, the logic level of the button pressed is 
     * at the low bit of g_logic_level.
    */
    g_logic_level |= (button->pressed_logic_level << button_cnt);
    button_cnt ++;

    return button_cnt;
}

/**
 * @brief Read all key values in one scan cycle
 * 
 * @param void
 * @return none
*/
static void button_read(void)
{
    uint8_t i;
    button_t* target;

    /* The button that was registered first, the button value is in the low position of raw_data */
    btn_type_t raw_data = 0;

    for(target = btn_head, i = button_cnt - 1;
        (target != NULL) && (target->usr_button_read != NULL);
        target = target->next, i--)
    {
        raw_data = raw_data | ((target->usr_button_read)(target) << i);
    }

    g_btn_status_reg = (~raw_data) ^ g_logic_level;
}

/**
 * @brief Handle all key events in one scan cycle.
 *        Must be used after 'button_read' API
 * 
 * @param void
 * @return Activated button count
*/
static uint8_t button_process(void)
{
    uint8_t i;
    uint8_t active_btn_cnt = 0;
    button_t* target;
    
    for (target = btn_head, i = button_cnt - 1; target != NULL; target = target->next, i--)
    {
        if (target->status > BTN_STAGE_DEFAULT)
        {
            target->scan_cnt ++;
            if (target->scan_cnt >= ((1 << (sizeof(target->scan_cnt) * 8)) - 1))
            {
                target->scan_cnt = target->long_hold_start_tick;
            }
        }

        switch (target->status)
        {
        case BTN_STAGE_DEFAULT: /* stage: default(button up) */
            if (BTN_IS_PRESSED(i)) /* is pressed */
            {
                target->scan_cnt = 0;
                target->click_cnt = 0;

                EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_DOWN);

                /* swtich to button down stage */
                target->status = BTN_STAGE_DOWN;
            }
            else
            {
                target->event = BTN_PRESS_NONE;
            }
            break;

        case BTN_STAGE_DOWN: /* stage: button down */
            if (BTN_IS_PRESSED(i)) /* is pressed */
            {
                if (target->click_cnt > 0) /* multiple click */
                {
                    if (target->scan_cnt > target->max_multiple_clicks_interval)
                    {
                        EVENT_SET_AND_EXEC_CB(target, 
                            target->click_cnt < BTN_PRESS_REPEAT_CLICK ? 
                                target->click_cnt :
                                BTN_PRESS_REPEAT_CLICK);

                        /* swtich to button down stage */
                        target->status = BTN_STAGE_DOWN;
                        target->scan_cnt = 0;
                        target->click_cnt = 0;
                    }
                }
                else if (target->scan_cnt >= target->long_hold_start_tick)
                {
                    if (target->event != BTN_PRESS_LONG_HOLD)
                    {
                        EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_LONG_HOLD);
                    }
                }
                else if (target->scan_cnt >= target->long_press_start_tick)
                {
                    if (target->event != BTN_PRESS_LONG_START)
                    {
                        EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_LONG_START);
                    }
                }
                else if (target->scan_cnt >= target->short_press_start_tick)
                {
                    if (target->event != BTN_PRESS_SHORT_START)
                    {
                        EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_SHORT_START);
                    }
                }
            }
            else /* button up */
            {
                if (target->scan_cnt >= target->long_hold_start_tick)
                {
                    EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_LONG_HOLD_UP);
                    target->status = BTN_STAGE_DEFAULT;
                }
                else if (target->scan_cnt >= target->long_press_start_tick)
                {
                    EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_LONG_UP);
                    target->status = BTN_STAGE_DEFAULT;
                }
                else if (target->scan_cnt >= target->short_press_start_tick)
                {
                    EVENT_SET_AND_EXEC_CB(target, BTN_PRESS_SHORT_UP);
                    target->status = BTN_STAGE_DEFAULT;
                }
                else
                {
                    /* swtich to multiple click stage */
                    target->status = BTN_STAGE_MULTIPLE_CLICK;
                    target->click_cnt ++;
                }
            }
            break;

        case BTN_STAGE_MULTIPLE_CLICK: /* stage: multiple click */
            if (BTN_IS_PRESSED(i)) /* is pressed */
            {
                /* swtich to button down stage */
                target->status = BTN_STAGE_DOWN;
                target->scan_cnt = 0;
            }
            else
            {
                if (target->scan_cnt > target->max_multiple_clicks_interval)
                {
                    EVENT_SET_AND_EXEC_CB(target, 
                        target->click_cnt < BTN_PRESS_REPEAT_CLICK ? 
                            target->click_cnt :
                            BTN_PRESS_REPEAT_CLICK);

                    /* swtich to default stage */
                    target->status = BTN_STAGE_DEFAULT;
                }
            }
            break;
        }
        
        if (target->status > BTN_STAGE_DEFAULT)
        {
            active_btn_cnt ++;
        }
    }
    
    return active_btn_cnt;
}

/**
 * button_event_read
 * 
 * @brief Get the button event of the specified button.
 * 
 * @param button: button structure instance
 * @return button event
*/
button_event_t button_event_read(button_t* button)
{
    return (button_event_t)(button->event);
}

/**
 * button_scan
 * 
 * @brief Start key scan.
 *        Need to be called cyclically within the specified period.
 *        Sample cycle: 5 - 20ms
 * 
 * @param void
 * @return Activated button count
*/
uint8_t button_scan(void)
{
    button_read();
    return button_process();
}
